//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.IdentityModel.Tokens;
using System.ServiceModel.Security.Tokens;
using System.Xml;
using System.ServiceModel.Security;
using System.Web.Security;
using System.Web;

namespace Shp.Security.BrokeredSender
{
    /// <summary> 
    /// Custom token provider. This class keeps the tokens outside of the channel 
    /// so they can be reused 
    /// </summary> 
    public class CustomIssuedSecurityTokenProvider : IssuedSecurityTokenProvider
    {
        private IssuedSecurityTokenProvider innerProvider;
        /// <summary> 
        /// Constructor 
        /// </summary> 

        public CustomIssuedSecurityTokenProvider(IssuedSecurityTokenProvider innerProvider)
            : base()
        {
            this.innerProvider = innerProvider;
            this.CacheIssuedTokens = innerProvider.CacheIssuedTokens;
            this.IdentityVerifier = innerProvider.IdentityVerifier;
            this.IssuedTokenRenewalThresholdPercentage = innerProvider.IssuedTokenRenewalThresholdPercentage;
            this.IssuerAddress = innerProvider.IssuerAddress;
            this.IssuerBinding = innerProvider.IssuerBinding;


            this.MaxIssuedTokenCachingTime = innerProvider.MaxIssuedTokenCachingTime;

            foreach (IEndpointBehavior behavior in innerProvider.IssuerChannelBehaviors)
            {
                this.IssuerChannelBehaviors.Add(behavior);
            }

            this.KeyEntropyMode = innerProvider.KeyEntropyMode;


            this.MessageSecurityVersion = innerProvider.MessageSecurityVersion;
            this.SecurityAlgorithmSuite = innerProvider.SecurityAlgorithmSuite;
            this.SecurityTokenSerializer = innerProvider.SecurityTokenSerializer;
            this.TargetAddress = innerProvider.TargetAddress;

            foreach (XmlElement parameter in innerProvider.TokenRequestParameters)
            {
                this.TokenRequestParameters.Add(parameter);
            }

            this.innerProvider.Open();
        }

        /// <summary> 
        /// Gets the security token 
        /// </summary> 
        /// <param name="timeout"></param> 
        /// <returns></returns> 

        protected override System.IdentityModel.Tokens.SecurityToken GetTokenCore(TimeSpan timeout)
        {
            System.IdentityModel.Tokens.SecurityToken securityToken = null;
            if (this.CacheIssuedTokens)
            {
                securityToken = SecurityTokenCacheStore.GetSecurityToken(this.innerProvider.IssuerAddress.Uri);
                if (securityToken == null || !IsServiceTokenTimeValid(securityToken))
                {
                    try
                    {
                        securityToken = innerProvider.GetToken(timeout);
                        SecurityTokenCacheStore.AddSecurityToken(this.innerProvider.IssuerAddress.Uri, securityToken);
                    }
                    catch (SecurityNegotiationException)
                    {
                        FormsAuthentication.SignOut();
                        string loginUrl = string.Format("{0}?{1}", FormsAuthentication.LoginUrl, "Error=sts");
                        HttpContext.Current.Response.Redirect(loginUrl, true);
                    }

                }
            }
            else
            {
                securityToken = innerProvider.GetToken(timeout);
            }

            return securityToken;
        }

        /// <summary> 
        /// Checks the token expiration. 
        /// A more complex algorithm can be used here to determine whether the token is valid or not. 
        /// </summary> 

        private bool IsServiceTokenTimeValid(SecurityToken serviceToken)
        {
            return (DateTime.UtcNow <= serviceToken.ValidTo.ToUniversalTime());
        }

        ~CustomIssuedSecurityTokenProvider()
        {
            this.innerProvider.Close();
        }

    }
}
